<template>
  <div id="app">
    <div id="left">
      <NewChat class="newChat" @newChatClick="newChatClick" />
      <History
        ref="history_tag"
        class="history"
        :historyTitleList
        :lastKey
        @deleteTitleClick="deleteTitleClick"
        @selectTitleClick="selectTitleClick"
      />
    </div>
    <div id="right">
      <Message
        ref="message_tag"
        :historys
        @sendClick="sendClick"
        @uploadClick="uploadClick"
        @stopGenerate="stopGenerate"
        @voiceClick="voiceClick"
      />
    </div>
  </div>
</template>

<script setup>
import Message from "./message";
import NewChat from "./newChat";
import History from "./history";
import { reactive, ref, onMounted, watch } from "vue";
/* -------------------------------------------------------------------------- */
/*                             newChat components                             */
/* -------------------------------------------------------------------------- */
// 新建会话
function newChatClick() {
  lastKey.value = null;
  historys.splice(0, historys.length);
}

/* -------------------------------------------------------------------------- */
/*                             history components                             */
/* -------------------------------------------------------------------------- */

const HISTORY_TITLE_LIST_NAME = "historyTitleList";
const HISTORY_LAST_KEY_NAME = "last_key";
let history_tag = ref(null);
let lastKey = ref(getOrDefalut(HISTORY_LAST_KEY_NAME, null));
let historyTitleList = reactive(getOrDefalut(HISTORY_TITLE_LIST_NAME, []));

function getOrDefalut(key, defalut) {
  let item = localStorage.getItem(key);
  return item ? JSON.parse(item) : defalut;
}
function generateKey() {
  return String(new Date().getTime());
}
watch(lastKey, () => {
  localStorage.setItem(HISTORY_LAST_KEY_NAME, lastKey.value);
});
watch(
  () => historyTitleList.length,
  () => {
    localStorage.setItem(
      HISTORY_TITLE_LIST_NAME,
      JSON.stringify(historyTitleList)
    );
  }
);

function deleteTitleClick(key) {
  let idx = -1;
  for (let i = 0; i < historyTitleList.length; i++) {
    if (historyTitleList[i].key === key) {
      idx = i;
      break;
    }
  }
  if (idx !== -1) {
    historyTitleList.splice(idx, 1);
  }
  historys.splice(0, historys.length);
  localStorage.removeItem(key);
  lastKey.value = null;
}
function selectTitleClick(key) {
  lastKey.value = key;
  let targetHistory = JSON.parse(localStorage.getItem(key));
  historys.splice(0, historys.length);
  historys.push(...targetHistory);
}

/* -------------------------------------------------------------------------- */
/*                              message component                             */
/* -------------------------------------------------------------------------- */

let message_tag = ref(null);
let historys = reactive(getOrDefalut(lastKey.value, []));

// History 通过Stream方式动态生成，不应该用watch
function storageHistorys(history) {
  if (!lastKey.value) {
    lastKey.value = generateKey();
    historyTitleList.push({ key: lastKey.value, title: history[0].content });
  }
  localStorage.setItem(lastKey.value, JSON.stringify(history));
}

let slideToBottom = () => {
  message_tag.value.slideToBottom();
};

onMounted(() => {
  // historys.push({ content: "123\n\n456 \n\n ```python\n\n def addxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx():return 0 \n\n ```", role: "assistant" })
});

let generate_state = false;
function stopGenerate() {
  let chat_done = message_tag.value.chat_done;
  chat_done();
  generate_state = false;
}

const fetchStream = async (url, jsondata) => {
  generate_state = true;
  let chat_start = message_tag.value.chat_start;
  let chat_done = message_tag.value.chat_done;
  // 必须先让GPT产生输出才能使图标旋转
  historys.push({ content: "", role: "assistant" });
  chat_start();

  await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: jsondata, // body data type must match "Content-Type" header
  })
    .then(async (response) => {
      const reader = response.body.getReader();
      while (true) {
        const { value, done } = await reader.read();
        if (done) {
          storageHistorys(historys); // 每次存储一组对话
          break; // 读取完毕
        } else {
          if (generate_state) {
            let delta = new TextDecoder().decode(value);
            // console.log(delta);
            historys[historys.length - 1].content += delta;
            slideToBottom();
          }
        }
      }
    })
    .catch((error) => {
      console.log(error);
      historys[historys.length - 1].content = "网络错误";
      storageHistorys(historys); // 每次存储一组对话
      slideToBottom();
      chat_done();
    })
    .finally(() => {
      chat_done();
      generate_state = false;
    });
};
function sendClick(data) {
  if (data) {
    let user_data = {
      content: data,
      role: "user",
    };
    historys.push(user_data);
    fetchStream(`${window.webConfig.baseUrl}/stream_chat`, JSON.stringify(historys));
  }
}

function voiceClick(data) {
  console.log("voice");
}

function uploadClick(data) {
  console.log("upload");
}
</script>

<style>
#app {
  display: flex;
  flex-direction: row;
}

#left {
  width: 15%;
  height: 100%;
  background-color: #1f2123;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
}

.newChat {
  height: 7%;
  margin-top: 5%;
}

.history {
  height: 93%;
}

#right {
  width: 85%;
  height: 100%;
  background-color: #353640;
}
</style>
